package com.weishi.project.training.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weishi.common.utils.DataAnalysisUtils;
import com.weishi.common.utils.DateUtils;
import com.weishi.common.utils.StringUtils;
import com.weishi.project.speedometer.vo.PropellerFrequencyData;
import com.weishi.project.speedometer.vo.PropellerFrequencyDataVo;
import com.weishi.project.speedometer.vo.RealTimeDataVo;
import com.weishi.project.training.domain.HeartRateCount;
import com.weishi.project.training.domain.ShipSpeedRecord;
import com.weishi.project.training.domain.ShipSpeedRecordLog;
import com.weishi.project.training.domain.TrainingGroupChannel;
import com.weishi.project.training.mapper.ShipSpeedRecordMapper;
import com.weishi.project.training.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 船速仪历史数据Service业务层处理
 */
@Service
public class ShipSpeedRecordServiceImpl extends ServiceImpl<ShipSpeedRecordMapper, ShipSpeedRecord> implements IShipSpeedRecordService {

    @Autowired
    private ShipSpeedRecordMapper shipSpeedRecordMapper;

    @Autowired
    private IShipSpeedRecordLogService shipSpeedRecordLogService;

    @Autowired
    private IHeartRateCountService heartRateCountService;

    @Autowired
    private ITrainingGroupChannelService trainingGroupChannelService;

    @Autowired
    private IAccelerationLogService accelerationLogService;

    @Autowired
    private IWitAccLogService witAccLogService;

    @Autowired
    Environment environment;

    /**
     * 查询船速仪历史数据列表
     */
    @Override
    public List<ShipSpeedRecord> selectShipSpeedRecordList(ShipSpeedRecord shipSpeedRecord) {
        List<ShipSpeedRecord> list = new ArrayList<>();
        if (shipSpeedRecord != null && shipSpeedRecord.getSegmentId() != 0) {
            ShipSpeedRecord ss = selectShipSpeedRecord(shipSpeedRecord);
            list.add(ss);
        } else {
            list = shipSpeedRecordMapper.selectShipSpeedRecordList(shipSpeedRecord);
        }
        return list;
    }

    @Override
    public ShipSpeedRecord selectShipSpeedRecord(ShipSpeedRecord shipSpeedRecord) {
        ShipSpeedRecord ret = new ShipSpeedRecord();
        ret.setTotalTime(0.0d);
        ret.setTotalDistance(0.0d);
        ret.setSegmentId(0);
        ret.setStraightLineDistance(0.0d);
        ret.setDatajson("");
        ret.setDatajsonPropellerFrequency("");
        List<ShipSpeedRecord> list = shipSpeedRecordMapper.selectShipSpeedRecordList(shipSpeedRecord);
        if (list != null && list.size() > 0) {
            for (ShipSpeedRecord ship : list) {
                ret.setTotalTime(ret.getTotalTime() + ship.getTotalTime());
                ret.setTotalDistance(ret.getTotalDistance() + ship.getTotalDistance());
                ret.setStraightLineDistance(ret.getStraightLineDistance() + ship.getStraightLineDistance());
                String dataJson = ret.getDatajson() + ship.getDatajson();
                ret.setDatajson(dataJson.replace("][", ","));
            }
            ret.setDatajsonPropellerFrequency(list.get(0).getDatajsonPropellerFrequency());
        }
        return ret;
    }

    @Override
    public List<PropellerFrequencyData> getPropellerFrequencyData(ShipSpeedRecord shipSpeedRecord) {
        List<PropellerFrequencyData> ret = new ArrayList<>();
        List<RealTimeDataVo> dataVos = shipSpeedRecordLogService.selectDataListByChannelId(shipSpeedRecord.getChannelId());
        int count = 4;
        return getPropellerFrequencyData(dataVos, count);
    }

    private List<PropellerFrequencyData> getPropellerFrequencyData(List<ShipSpeedRecordLog> logs) {
        List<PropellerFrequencyData> ret = new ArrayList<>();
        for (ShipSpeedRecordLog shipSpeedRecordLog : logs) {
            PropellerFrequencyData data = new PropellerFrequencyData();
            data.setDistanceTotal(shipSpeedRecordLog.getDistance());
            data.setRealTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, new Date(Long.parseLong(shipSpeedRecordLog.getTimp()))));
            data.setAveragePropeller(shipSpeedRecordLog.getPropellerFrequency());
            ret.add(data);
        }

        return ret;
    }

    /**
     * @param dataVos
     * @param count   一次桨频算一次平均桨频
     * @return
     */
    private List<PropellerFrequencyData> getPropellerFrequencyData(List<RealTimeDataVo> dataVos, int count) {
        List<PropellerFrequencyDataVo> list = getImportantPoint(dataVos);
        List<PropellerFrequencyData> ret = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            PropellerFrequencyData data = new PropellerFrequencyData();
            data.setCount(count);
            if (list.get(i).getType() == -1) {
                data.setStartTime(list.get(i).getRealTime());
                if ((i + count * 2) < list.size()) {
                    data.setDistanceTotal(list.get(i + count * 2).getDistanceTotal());
                    data.setDistance(list.get(i + count * 2).getDistanceTotal() - list.get(i).getDistanceTotal());
                    data.setEndTime(list.get(i + count * 2).getRealTime());
                    double minute = (getTime(data.getEndTime()) - getTime(data.getStartTime())) / 1000;
                    data.setAveragePropeller(count / (minute / 60.0));
                } else {
                    PropellerFrequencyDataVo temp = (i + count * 2) <= list.size() ? list.get(i + count) : list.get(i);
                    data.setEndTime(temp.getRealTime());
                    data.setDistanceTotal(temp.getDistanceTotal());
                    data.setDistance(temp.getDistanceTotal() - list.get(i).getDistanceTotal());
                    double minute = (getTime(data.getEndTime()) - getTime(data.getStartTime())) / 60000;
                    double averagePropeller = minute != 0 ? 1 / minute : 0.0;
                    data.setAveragePropeller(averagePropeller);
                }
                data.setRealTime(data.getEndTime());

                ret.add(data);
            }
        }
        return ret;
    }

    /**
     * 获取时间戳
     *
     * @param utc
     * @return
     */
    private long getTime(String utc) {
        Date dateTime = DateUtils.dateTime("HH:mm:ss.SSS", utc);
        return dateTime.getTime();
    }

    @Override
    public void addShipSpeedRecord(String shipSpeedId, Integer channelId) {
        //查询所有分段
        int segmentId = 0;
        ShipSpeedRecordLog params = new ShipSpeedRecordLog();
        params.setShipSpeedSn(shipSpeedId);
        params.setChannelId(channelId);
        List<ShipSpeedRecordLog> logs = shipSpeedRecordLogService.selectShipSpeedRecordLogList(params);
        if (logs != null && logs.size() == 0) {
            return;
        }
        Date utc = new Date(Long.parseLong(logs.get(0).getTimp()));

        TrainingGroupChannel channel = trainingGroupChannelService.getById(channelId);

        long fristTime = channel.getStime() != null ? channel.getStime().getTime() : utc.getTime();

        List<HeartRateCount> heartRateCounts = heartRateCountService.qryCountByChannel(channelId);
        //未手动计次 （系统自动分段）
        if (logs!=null && logs.size()>0 && heartRateCounts != null && heartRateCounts.size() == 1) {
            HeartRateCount end = heartRateCounts.get(0);
            int index = 1;
            if(channel.getTrainingMode()==null || channel.getTrainingMode()==0){
                int kilometer = getSubsectionKilometers(channel.getRowingKilometers());
                int startKilometer = 0;
                int endKilometer = kilometer;
                ShipSpeedRecordLog temp = logs.get(0);
                for(ShipSpeedRecordLog logTemp : logs){
                    //最后一个计次按照
                    if(channel.getRowingKilometers()-endKilometer <= kilometer/2){
                        break;
                    }

                    if(logTemp.getDistance() >= endKilometer && temp.getDistance() <= endKilometer){
                        //判断更接近的点
                        temp = (logTemp.getDistance()-endKilometer)>(endKilometer - temp.getDistance()) ? temp : logTemp;
                        HeartRateCount heart = new HeartRateCount();
                        heart.setStage(index++);
                        heart.setTotalDistance((double)endKilometer);
                        heart.setChannelId(channelId);
                        heart.setCtime(new Date());
                        heart.setTime(logTemp.getCtime());
                        heart.setShipSpeedId(channel.getSeat1());
                        heart.setGroupId(channel.getGroupId());
                        long pointAchievement = Long.parseLong(temp.getTimp()) - fristTime;
                        heart.setPointAchievement(DateUtils.getStringTime_HHmmss_SS(pointAchievement));
                        startKilometer += kilometer;
                        endKilometer = startKilometer + kilometer;
                        heartRateCountService.saveAndAddRecord(heart);
                        heartRateCounts.add(heart);
                    }
                    temp = logTemp;
                }
            }else{
                long classSecond = (long) ((channel.getRowingClassHour()/4)*60);
                long startSecond = fristTime;
                long endSecond = startSecond + classSecond;
                ShipSpeedRecordLog temp = logs.get(0);
                for(ShipSpeedRecordLog logTemp : logs){
                    long logTempSecond = Long.parseLong(logTemp.getTimp())/1000;
                    long tempSecond = Long.parseLong(temp.getTimp())/1000;
                    if(logTempSecond >= endSecond && tempSecond <= endSecond){
                        //判断更接近的点
                        temp = (logTempSecond-endSecond)>(endSecond - tempSecond) ? temp : logTemp;
                        HeartRateCount heart = new HeartRateCount();
                        heart.setStage(index++);
                        heart.setChannelId(channelId);
                        heart.setCtime(new Date());
                        heart.setTime(logTemp.getCtime());
                        heart.setGroupId(channel.getGroupId());
                        long pointAchievement = Long.parseLong(temp.getTimp()) - fristTime;
                        heart.setPointAchievement(DateUtils.getStringTime(pointAchievement));
                        startSecond += classSecond;
                        endSecond = startSecond + classSecond;
                        heartRateCountService.saveAndAddRecord(heart);
                        heartRateCounts.add(heart);
                    }
                }
            }


            end.setStage(index);
            heartRateCountService.updateById(end);

            heartRateCounts = heartRateCountService.qryCountByChannel(channelId);
        }

        Double averageSpeed = 0.0;  //全程平均船速
        Double maxSpeed = 0.0;  //全程最大船速
        String sectionalSpeed = "";  //分段船速
        Double totalDistance = 0.0;  //总里程

        Double averageFrequency = 0.0;  //全程平均桨频
        int pulpNumber = 0;  //全程总桨数
        String propellerFrequencyTemp = "";  //分段桨频
        DecimalFormat df = new DecimalFormat("######0.00");


        if (heartRateCounts != null && heartRateCounts.size() > 0) {
            long startTime = fristTime;
            boolean flagDatajsonPropellerFrequency = true;
            for (HeartRateCount heart : heartRateCounts) {
                long endTime = fristTime + DateUtils.getLongTime(heart.getPointAchievement());
                List<ShipSpeedRecordLog> logTemp = getLogsByTime(logs, startTime, endTime, fristTime);
                ShipSpeedRecord shipSpeedRecord = addShipSpeedRecord(logTemp, shipSpeedId, channelId, heart.getId());

                List<PropellerFrequencyData> propellerFrequencyDataList = witAccLogService.qryPropellerFrequencyDate(shipSpeedId, channelId,fristTime);
                if(StringUtils.isEmpty(propellerFrequencyDataList)){
                    propellerFrequencyDataList = accelerationLogService.qryPropellerFrequencyDate(shipSpeedId, channelId,fristTime);
                }
                //计算分段桨频
                int propellerFrequency = getSubsectionPropellerFrequency(propellerFrequencyDataList,startTime,endTime,channel.getShipSpeedId());
                shipSpeedRecord.setPropellerFrequency(propellerFrequency);
                if (flagDatajsonPropellerFrequency) {
                    shipSpeedRecord.setDatajsonPropellerFrequency(JSONArray.toJSONString(propellerFrequencyDataList));
                    shipSpeedRecordMapper.updateById(shipSpeedRecord);
                    flagDatajsonPropellerFrequency = false;
                    pulpNumber = propellerFrequencyDataList.size();
                }
                //更新一下 HeartRateCount
                heart.setShipSpeed(shipSpeedRecord.getAverageSpeed());
                heart.setShipSpeedId(shipSpeedRecord.getShipSpeedId());
                if(heart.getTotalDistance()==null || heart.getTotalDistance()==0) heart.setTotalDistance(shipSpeedRecord.getTotalDistance());
                totalDistance = shipSpeedRecord.getTotalDistance();
                averageFrequency += propellerFrequency;
                heart.setPropellerFrequency(propellerFrequency + "");

                propellerFrequencyTemp += heart.getPropellerFrequency() + " ";

                sectionalSpeed += df.format(heart.getShipSpeed()) + " ";
                averageSpeed += heart.getShipSpeed();

                if (heart.getShipSpeed() > maxSpeed) {
                    maxSpeed = heart.getShipSpeed();
                }

                heartRateCountService.updateById(heart);
                startTime = endTime;
            }
        } else {
            addShipSpeedRecord(logs, shipSpeedId, channelId, segmentId);
        }

        //同步航道的信息
        TrainingGroupChannel trainingGroupChannel = trainingGroupChannelService.getById(channelId);
        trainingGroupChannel.setAverageSpeed(heartRateCounts != null ? df.format(averageSpeed / heartRateCounts.size()) + "" : "0");
        trainingGroupChannel.setSpeed(Double.parseDouble(trainingGroupChannel.getAverageSpeed()));
        trainingGroupChannel.setMaxSpeed(df.format(maxSpeed));
        //trainingGroupChannel.setAchievement(String.valueOf(totalDistance));
        trainingGroupChannel.setSectionalSpeed(sectionalSpeed.trim());
        trainingGroupChannel.setAverageFrequency(heartRateCounts.size() > 0 ? averageFrequency / heartRateCounts.size() : 0);
        trainingGroupChannel.setPulpNumber(pulpNumber);
        trainingGroupChannel.setPropellerFrequency(propellerFrequencyTemp.trim());
        trainingGroupChannelService.updateById(trainingGroupChannel);
    }

    private ShipSpeedRecord addShipSpeedRecord(List<ShipSpeedRecordLog> logs, String shipSpeedId, Integer channelId, int segmentId) {
        List<RealTimeDataVo> dataVos = ShipSpeedRecordLog.getRealTimeData(logs);

        ShipSpeedRecord shipSpeedRecord = new ShipSpeedRecord();
        double speedTotal = 0;
        shipSpeedRecord.setDatajsonPropellerFrequency("");
        if (dataVos == null || dataVos.size() == 0) {
            shipSpeedRecord.setTotalTime(0.0);
            shipSpeedRecord.setSegmentId(segmentId);
            shipSpeedRecord.setChannelId(channelId);
            shipSpeedRecord.setShipSpeedId(shipSpeedId);
            shipSpeedRecord.setDatajson("[]");
            shipSpeedRecord.setTotalDistance(0.0);
            shipSpeedRecord.setStraightLineDistance(0.0);
            shipSpeedRecord.setTotalTime(1.0);
            shipSpeedRecord.setAverageSpeed(0.0);
//            shipSpeedRecord.setPropellerFrequency(getPropellerFrequency(logs));
            return shipSpeedRecord;
        }
        RealTimeDataVo last = dataVos.get(dataVos.size() - 1);
//        double averageSpeed = last.getDistanceTotal()/(last.getTime());
        for(ShipSpeedRecordLog shipSpeedRecordLog : logs){
            speedTotal += shipSpeedRecordLog.getSpeed();
        }
        double averageSpeed = speedTotal/logs.size();

        shipSpeedRecord.setSegmentId(segmentId);
        shipSpeedRecord.setChannelId(channelId);
        shipSpeedRecord.setShipSpeedId(shipSpeedId);
        shipSpeedRecord.setCtime(new Date());
        shipSpeedRecord.setDatajson(JSONArray.toJSONString(dataVos));
        shipSpeedRecord.setTotalDistance(last.getDistanceTotal());
        shipSpeedRecord.setStraightLineDistance(last.getStraightLineDistance());
        shipSpeedRecord.setTotalTime(last.getTime());
        shipSpeedRecord.setPropellerFrequency(getPropellerFrequency(logs));
        shipSpeedRecord.setAverageSpeed(averageSpeed);
        shipSpeedRecordMapper.insert(shipSpeedRecord);
        return shipSpeedRecord;
    }

    /**
     * 获取指定时间区域的数据
     *
     * @param logs
     * @param startTime
     * @param endTime
     * @return
     */
    private List<ShipSpeedRecordLog> getLogsByTime(List<ShipSpeedRecordLog> logs, long startTime, long endTime, long fristTime) {
        List<ShipSpeedRecordLog> logsTemp = new ArrayList<>();
        ShipSpeedRecordLog prev = null;
        boolean prevAddFalg = false;
        for (ShipSpeedRecordLog shipSpeedRecordLog : logs) {
            long time = Long.parseLong(shipSpeedRecordLog.getTimp());
            shipSpeedRecordLog.setUtc(DateUtils.getStringTime(time - fristTime));
            if (startTime <= time && endTime >= time) {
                if (prev != null && !prevAddFalg) {
                    //添加上一个节点
                    logsTemp.add(prev);
                    prevAddFalg = true;
                }
                logsTemp.add(shipSpeedRecordLog);
            }
            if (!prevAddFalg) prev = shipSpeedRecordLog;
        }
        return logsTemp;
    }

    /**
     * 根据速度曲线计算桨频
     *
     * @param dataVos
     * @return
     */
    private int getPropellerFrequencyOld(List<RealTimeDataVo> dataVos) {
        List<Double> distances = new ArrayList<>();
        for (RealTimeDataVo realTimeDataVo : dataVos) {
            distances.add(realTimeDataVo.getDistance());
        }
        return DataAnalysisUtils.getPeakNumPlus(distances);
    }

    private int getPropellerFrequency(List<ShipSpeedRecordLog> dataVos) {
        int temp = 0;
        for (ShipSpeedRecordLog shipSpeedRecordLog : dataVos) {
            temp += shipSpeedRecordLog.getPropellerFrequency();
        }
        return dataVos.size() == 0 ? 0 : temp / dataVos.size();
    }

    /**
     * 获取波峰波谷
     *
     * @param dataVos
     * @return
     */
    private List<PropellerFrequencyDataVo> getImportantPoint(List<RealTimeDataVo> dataVos) {
        List<PropellerFrequencyDataVo> dataVoList = new ArrayList<>();

        List<Double> distances = new ArrayList<>();
        for (RealTimeDataVo realTimeDataVo : dataVos) {
            distances.add(realTimeDataVo.getDistance());
        }
        double[] data = new double[dataVos.size()];
        int index = 0;
        for (Double aa : distances) {
            data[index++] = aa;
        }
        int peak = 0;

        try {
            int[] PeakAndTrough = new int[data.length];

            //需要三个不同的值进行比较，取lo,mid，hi分别为三值
            for (int lo = 0, mid = 1, hi = 2; hi < data.length; hi++) {
                //先令data[lo]不等于data[mid]
                while (mid < data.length && data[mid] == data[lo]) {
                    mid++;
                }

                hi = mid + 1;

                //令data[hi]不等于data[mid]
                while (hi < data.length && data[hi] == data[mid]) {
                    hi++;
                }

                if (hi >= data.length) {
                    break;
                }

                //检测是否为峰值
                if (data[mid] > data[lo] && data[mid] > data[hi]) {
                    PeakAndTrough[mid] = 1;       //1代表波峰
                } else if (data[mid] < data[lo] && data[mid] < data[hi]) {
                    PeakAndTrough[mid] = -1;      //-1代表波谷
                }

                lo = mid;
                mid = hi;
            }

            //计算均值
            float ave = 0;
            for (int i = 0; i < data.length; i++) {
                ave += data[i];
            }
            ave /= data.length;

            //排除大于均值的波谷和小于均值的波峰
            for (int i = 0; i < PeakAndTrough.length; i++) {
                if ((PeakAndTrough[i] > 0 && data[i] < ave) || (PeakAndTrough[i] < 0 && data[i] > ave)) {
                    PeakAndTrough[i] = 0;
                }
            }

            //统计波谷数量
            for (int i = 0; i < PeakAndTrough.length; i++) {
                if (PeakAndTrough[i] != 0) {
                    PropellerFrequencyDataVo vo = new PropellerFrequencyDataVo();
                    vo.setType(PeakAndTrough[i]);
                    vo.setRealTime(dataVos.get(i).getRealTime());
                    vo.setDistanceTotal(dataVos.get(i).getDistanceTotal());
                    dataVoList.add(vo);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataVoList;
    }

    /**
     * 查询分段桨频
     * @param propellerFrequencyDataList
     * @param startTime
     * @param endTime
     * @return
     */
    private int getSubsectionPropellerFrequency(List<PropellerFrequencyData> propellerFrequencyDataList, long startTime, long endTime,String devId) {
        int count = 0;
        long etime = 0;
        long stime = 0;
        for(PropellerFrequencyData propellerFrequencyData : propellerFrequencyDataList){
            long flagTime = Long.parseLong(propellerFrequencyData.getEndTime());
            if(startTime<=flagTime && endTime>=flagTime){
                count++;
                if(stime==0)stime = flagTime;
                etime = flagTime;
            }
        }

        String boatType = environment.getProperty("system.version");
        //测试环境的艇种根据登陆账号区分
        if(boatType.equals("demo")){
            boatType = devId.contains("HT") ? "h" : devId.contains("PT") ? "p" : "s";
        }
        if(boatType.equals("p")){
            count = count*2;
        }

        double p = endTime-startTime==0?0:count/((endTime-startTime)/1000d/60d);
        return (int)Math.round(p);
    }

    /**
     * 获取自动分段距离
     * @param kilometers 计划划行公里数
     * @return
     */
    private int getSubsectionKilometers(int kilometers){
        int subsectionKilometers =1000;
        if(kilometers<300){
            return 50;
        }else if(kilometers<700){
            return 100;
        }else if(kilometers<1000){
            return 250;
        }else if(kilometers<=2000){
            return 500;
        }
        return subsectionKilometers;
    }

}